home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 January / macpower199701.bin / AMUG / Programming_10 / WASTE 1.3a1.sit / WASTE 1.3a1 Distribution / WASTE 1.3a1 / WELowLevelEditing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-23  |  25.6 KB  |  934 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELowLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Low-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal Boolean _WEIsWordRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  18. {
  19.     // _WEIsWordRange returns true if the specified range is a word range,
  20.     // i.e. if it would be possible to select it by double-clicking and (optionally) dragging.
  21.  
  22.     SInt32 wordStart, wordEnd;
  23.  
  24.     // determine if rangeStart is at the beginning of a word
  25.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  26.     if (rangeStart == wordStart)
  27.     {
  28.  
  29.         // determine if rangeEnd is at the end of a word
  30.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  31.         return (rangeEnd == wordEnd);
  32.     }
  33.     return false;
  34. }
  35.  
  36. pascal Boolean _WEIsPunct(SInt32 offset, WEHandle hWE)
  37. {
  38.     SInt16 cType;
  39.  
  40.     cType = WECharType(offset, hWE);
  41.     if ((cType & smcTypeMask) == smCharPunct)
  42.     {
  43.         cType &= smcClassMask;
  44.         if ((cType == smPunctNormal) || (cType == smPunctBlank))
  45.             return true;
  46.     }
  47.     return false;
  48. }
  49.  
  50. pascal void _WEIntelligentCut(SInt32 *rangeStart, SInt32 *rangeEnd, WEHandle hWE)
  51. {
  52.  
  53.     // _WEIntelligentCut is called by other WASTE routines to determine the actual
  54.     // range to be deleted when weFIntCutAndPaste is enabled.
  55.     // On entry, rangeStart and rangeEnd specify the selection range visible to the user.
  56.     // On exit, rangeStart and rangeEnd specify the actual range to be removed.
  57.  
  58.     // do nothing if the intelligent cut-and-paste feature is disabled
  59.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  60.         return;
  61.  
  62.     // intelling cut-&-paste rules should be applied only to word ranges...
  63.     if (!_WEIsWordRange(*rangeStart, *rangeEnd, hWE))
  64.         return;
  65.  
  66.     // ...without punctuation characters at the beginning or end
  67.     if (_WEIsPunct(*rangeStart, hWE))
  68.         return;
  69.     if (_WEIsPunct(*rangeEnd - 1, hWE))
  70.         return;
  71.  
  72.     // if the character preceding the selection range is a space, discard it
  73.     if (WEGetChar(*rangeStart - 1, hWE) == kSpace)
  74.         (*rangeStart)--;
  75.     // else, if the character following the selection range is a space, discard it
  76.     else if (WEGetChar(*rangeEnd, hWE) == kSpace)
  77.         (*rangeEnd)++;
  78.  
  79. }
  80.  
  81. pascal SInt16 _WEIntelligentPaste(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  82. {
  83.     SInt16 retval;
  84.  
  85.     // _WEIntelligentPaste is called by other WASTE routines to determine whether
  86.     // an additional space character should be added (before or after) after inserting
  87.     // new text (usually from the Clipboard or from a drag).
  88.  
  89.     retval = weDontAddSpaces;
  90.  
  91.     // do nothing unless the intelligent cut-and-paste feature is enabled
  92.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  93.         return retval;
  94.  
  95.     // extra spaces will be added only if the pasted text looks like a word range,
  96.     // without punctuation characters at the beginning or at the end
  97.     if (_WEIsPunct(rangeStart, hWE))
  98.         return retval;
  99.     if (_WEIsPunct(rangeEnd - 1, hWE))
  100.         return retval;
  101.  
  102.     // if the character on the left of the pasted text is a punctuation character
  103.     // and the character on the right isn't,  add a space on the right, and vice versa
  104.     if (_WEIsPunct(rangeStart - 1, hWE))
  105.     {
  106.         if (!_WEIsPunct(rangeEnd, hWE))
  107.         {
  108.             retval = weAddSpaceOnRightSide;
  109.         }
  110.     }
  111.     else if (_WEIsPunct(rangeEnd, hWE))
  112.     {
  113.         retval = weAddSpaceOnLeftSide;
  114.     }
  115.  
  116.     return retval;
  117. }
  118.  
  119. pascal OSErr _WEInsertRun(SInt32 runIndex, SInt32 offset, SInt32 styleIndex, WEPtr pWE)
  120. {
  121.  
  122.     // Insert a new element in the style run array, at the specified runIndex position.
  123.     // The new element consists of the pair <offset, styleIndex>.
  124.  
  125.     RunArrayElement element;
  126.     OSErr err;
  127.  
  128.     // prepare the element record to be inserted in the array
  129.     element.runStart = offset;
  130.     element.styleIndex = styleIndex;
  131.  
  132.     // do the insertion
  133.     if ((err = _WESplice((Handle) pWE->hRuns, &element, sizeof(element), (runIndex + 1) * sizeof(element))) != noErr)
  134.         return err;
  135.  
  136.     // increment style run count
  137.     pWE->nRuns++;
  138.  
  139.     // increment the reference count field of the style table element
  140.     // referenced by the newly inserted style run
  141.     (*pWE->hStyles)[styleIndex].refCount++;
  142.  
  143.     return noErr;
  144. }
  145.  
  146. pascal void _WERemoveRun(SInt32 runIndex, WEPtr pWE)
  147. {
  148.     StyleTableElementPtr pStyle;
  149.  
  150.     // get a pointer to the style table element referenced by the style run
  151.     pStyle = *pWE->hStyles + (*pWE->hRuns)[runIndex].styleIndex;
  152.  
  153.     // decrement the reference count field of the style table element
  154.     // referenced by the style run to be removed
  155.     pStyle->refCount--;
  156.  
  157. #if WASTE_OBJECTS
  158.     // dispose of the embedded object (if any)
  159.     if (pStyle->info.runStyle.tsObject != nil)
  160.         _WEFreeObject(pStyle->info.runStyle.tsObject);
  161. #endif
  162.  
  163.     // remove a "slot" from the run array
  164.     _WESplice((Handle) pWE->hRuns, nil, - sizeof(RunArrayElement), runIndex * sizeof(RunArrayElement));
  165.  
  166.     // decrement style run count
  167.     pWE->nRuns--;
  168. }
  169.  
  170. pascal void _WEChangeRun(SInt32 runIndex, SInt32 newStyleIndex, Boolean keepOld, WEPtr pWE)
  171. {
  172.     // change the styleIndex field of the specified element of the style run array
  173.  
  174.     SInt32 oldStyleIndex;
  175.     StyleTableElementPtr oldStyle, newStyle;
  176.  
  177.     // do the change
  178.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  179.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  180.  
  181.     // get pointers to old and new style table elements
  182.     oldStyle = *pWE->hStyles + oldStyleIndex;
  183.     newStyle = *pWE->hStyles + newStyleIndex;
  184.  
  185.     // increment the reference count field of the new style table element
  186.     newStyle->refCount++;
  187.  
  188.     // decrement the reference count field of the old style table element
  189.     oldStyle->refCount--;
  190.  
  191. #if WASTE_OBJECTS
  192.     // dispose of the embedded object (if any) unless it is again referenced in the new style
  193.     if (!keepOld)
  194.     {
  195.         WEObjectDescHandle oldObject = oldStyle->info.runStyle.tsObject;
  196.  
  197.         if ((oldObject != nil) && (oldObject != newStyle->info.runStyle.tsObject))
  198.             _WEFreeObject(oldObject);
  199.     }
  200. #endif
  201.  
  202. }
  203.  
  204. pascal OSErr _WENewStyle(const WETextStyle *ts, SInt32 *styleIndex, WEPtr pWE)
  205. {
  206.     // given the specified WETextStyle record, find the corresponding entry
  207.     // in the style table (create a new entry if necessary), and return its index
  208.  
  209.     StyleTableElementPtr pElement;
  210.     StyleTableElement element;
  211.     SInt32 index;
  212.     SInt32 unusedIndex;
  213.     OSErr err;
  214.  
  215.     // see if the given style already exists in the style table
  216.     // while scanning the table, also remember the position of the first unused style, if any
  217.     unusedIndex = -1;
  218.     pElement = *pWE->hStyles;
  219.     for ( index = 0 ; index < pWE->nStyles ; index++ )
  220.     {
  221.         // check for entries which aren't referenced and can be recycled
  222.         if (pElement->refCount == 0)
  223.             unusedIndex = index;
  224.  
  225.         // perform a bitwise comparison between the current element and the specified style
  226.         // (actually, we ignore metrics information)
  227.         else if (_WEBlockCmp(&pElement->info.runStyle, ts, sizeof(WETextStyle)))
  228.         {
  229.             // found: style already present
  230.             *styleIndex = index;
  231.             return noErr;
  232.         }
  233.         pElement++;
  234.     } // for
  235.  
  236.     // the specified style doesn't exist in the style table
  237.     // since this is a new entry, we have to calculate font metrics information
  238.     element.info.runStyle = *ts;
  239.     _WEFillFontInfo(pWE->port, &element.info);
  240. /*
  241.     // set the high bit of tsFlags if attributes specify a right-to-left run
  242.     if (BTST(pWE->flags, weFBidirectional))
  243.     {
  244.         if (GetScriptVariable(FontToScript(element.info.runStyle.tsFont), smScriptRight) != 0)
  245.         {
  246.             BSET(element.info.runStyle.tsFlags, tsRightToLeft);
  247.         }
  248.     }
  249. */
  250.     // see if we can recycle an unused entry
  251.     if (unusedIndex >= 0)
  252.     {
  253.         index = unusedIndex;
  254.         (*pWE->hStyles)[index].info = element.info;
  255.     }
  256.     else
  257.     {
  258.         // no reusable entry: we have to append a new element at the end of the table
  259.         element.refCount = 0;
  260.         if ((err = _WESplice((Handle) pWE->hStyles, &element, sizeof(element), -1)) != noErr)
  261.             return err;
  262.  
  263.         // update style count in the WE record
  264.         pWE->nStyles++;
  265.     }
  266.  
  267.     // return the index to the new element
  268.     *styleIndex = index;
  269.     return noErr;
  270. }
  271.  
  272. pascal OSErr _WERedraw(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  273. {
  274.     WEPtr pWE = *hWE;        // assume WE record is already locked
  275.     LineArrayPtr pLines;
  276.     SInt32 startLine, endLine;
  277.     SInt32 oldTextHeight, newTextHeight;
  278.     LongRect r;
  279.     Rect viewRect, updateRect;
  280.     RgnHandle saveClip;
  281.     GrafPtr savePort;
  282.     OSErr err;
  283. #if WASTE_REDRAW_SPEED
  284.     LongRect scrollRect;
  285.     RgnHandle updateRgn,
  286.               utilRgn;
  287. #endif
  288.  
  289.     // do nothing if recalculation has been inhibited
  290.     if (!BTST(pWE->features, weFInhibitRecal))
  291.     {
  292.         // hide the caret
  293. #if WASTE_REDRAW_SPEED
  294.         BCLR(pWE->flags, weFCaretVisible);
  295. #else
  296.         if (BTST(pWE->flags, weFCaretVisible))
  297.         {
  298.             _WEBlinkCaret(hWE);
  299.         }
  300. #endif
  301.  
  302.         // remember total text height
  303.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  304.  
  305.         // find line range affected by modification
  306.         startLine = WEOffsetToLine(rangeStart, hWE);
  307.         endLine = WEOffsetToLine(rangeEnd, hWE);
  308.  
  309.         // recalculate line breaks starting from startLine
  310.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) != noErr)
  311.         {
  312.             goto cleanup;
  313.         }
  314.  
  315.         // recalculate slops
  316.         _WERecalSlops(startLine, endLine, hWE);
  317.  
  318.         // do nothing if redrawing has been inhibited
  319.         if (!BTST(pWE->features, weFInhibitRedraw))
  320.         {
  321.  
  322.             // calculate new total text height
  323.             newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  324.  
  325.             // calculate the rectangle to redraw (in long coordinates)
  326.             r.left = -SHRT_MAX;
  327.             r.right = SHRT_MAX;
  328.             pLines = *pWE->hLines;
  329.             r.top = pLines[startLine].lineOrigin;
  330.  
  331. #if WASTE_REDRAW_SPEED
  332.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  333.             // otherwise we must redraw all lines from startLine on
  334.  
  335.             if (endLine < pWE->nLines - 1)
  336.                  r.bottom = pLines[endLine + 1].lineOrigin;
  337.             else
  338.                 r.bottom = newTextHeight;
  339.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  340.  
  341.             if (newTextHeight == oldTextHeight)
  342.                 WELongRectToRect(&r, &updateRect);
  343.             else
  344.             {
  345.                 /*      Instead of scrolling the lines below the deleted text up by redrawing them,
  346.                  *      use scroll bits to move the displayed text up.
  347.                  */
  348.  
  349.                 scrollRect = pWE->viewRect;
  350.                 if (newTextHeight > oldTextHeight)
  351.                     scrollRect.top = pLines[startLine + 1].lineOrigin + pWE->destRect.top;
  352.                 else
  353.                     scrollRect.top = pLines[startLine].lineOrigin + pWE->destRect.top;
  354.                 WELongRectToRect(&scrollRect, &updateRect);
  355.                 updateRgn = NewRgn();
  356.                 ScrollRect(&updateRect, 0, newTextHeight - oldTextHeight, updateRgn);
  357.  
  358.                 //      Redraw the exposed region (caused by a scroll up)
  359.  
  360.                 WELongRectToRect(&r, &updateRect);
  361.                 utilRgn = NewRgn();
  362.                 RectRgn(utilRgn, &updateRect);
  363.                 DiffRgn(updateRgn, utilRgn, updateRgn);
  364.                 DisposeRgn(utilRgn);
  365.                 WEUpdate(updateRgn, hWE);
  366.                 DisposeRgn(updateRgn);
  367.             }
  368. #else
  369.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  370.             // otherwise we must redraw all lines from startLine on
  371.             if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1))
  372.             {
  373.                 r.bottom = pLines[endLine + 1].lineOrigin;
  374.             }
  375.             else if (newTextHeight < oldTextHeight)
  376.             {
  377.                 r.bottom = oldTextHeight;
  378.             }
  379.             else
  380.             {
  381.                 r.bottom = newTextHeight;
  382.             }
  383.  
  384.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  385.  
  386.             // calculate the intersection between this rectangle and the view rectangle
  387.             WELongRectToRect(&r, &updateRect);
  388. #endif
  389.             WELongRectToRect(&pWE->viewRect, &viewRect);
  390.  
  391.             if (SectRect(&updateRect, &viewRect, &updateRect))
  392.             {
  393.                 // set up the port and the clip region
  394.                 GetPort(&savePort);
  395.                 SetPort(pWE->port);
  396.  
  397.                 // set the clip region to updateRect
  398.                 saveClip = NewRgn();
  399.                 GetClip(saveClip);
  400.                 ClipRect(&updateRect);
  401.  
  402.                 // we only really need to redraw the visible lines
  403.                 startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  404.                 endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  405.  
  406.                 // redraw the lines (pass true in the doErase parameter)
  407.                 _WEDrawLines(startLine, endLine, true, hWE);
  408.  
  409.                 // erase the portion of the update rectangle below the last line (if any)
  410.                 pLines = *pWE->hLines;
  411.                 updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  412.                 if (updateRect.top < updateRect.bottom)
  413.                 {
  414.                     CallWEEraseProc(&updateRect, hWE, pWE->eraseHook);
  415.                 }
  416.  
  417.                 // restore the clip region
  418.                 SetClip(saveClip);
  419.                 DisposeRgn(saveClip);
  420.  
  421.                 // restore the port
  422.                 SetPort(savePort);
  423.  
  424.                 // redraw the caret or the selection range
  425.                 if (pWE->selStart < pWE->selEnd)
  426.                 {
  427.                     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  428.                 }
  429.                 else
  430.                 {
  431.                     _WEBlinkCaret(hWE);
  432.                 }
  433.             } // if SectRect
  434.  
  435.             // scroll the selection range into view
  436.             WESelView(hWE);
  437.  
  438.         } // if redraw not inhibited
  439.     } // if recal not inhibited
  440.  
  441.     // clear result code
  442.     err = noErr;
  443.  
  444. cleanup:
  445.     // return result code
  446.     return err;
  447. }
  448.  
  449. pascal OSErr WECalText(WEHandle hWE)
  450. {
  451.     Boolean saveWELock;
  452.     OSErr err;
  453.  
  454.     // lock WE record
  455.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  456.  
  457. #if WASTE_WECALTEXT_DOES_REDRAW
  458.  
  459.     // recalculate line breaks & slops and redraw the text
  460.     err = _WERedraw(0, LONG_MAX, hWE);
  461.  
  462. #else
  463.  
  464.     {
  465.         SInt32 startLine = 0;
  466.         SInt32 endLine = LONG_MAX;
  467.  
  468.         // recalculate line breaks & slops without redrawing anything
  469.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) == noErr)
  470.             _WERecalSlops(startLine, endLine, hWE);
  471.     }
  472.  
  473. #endif
  474.  
  475.     // unlock the WE record
  476.     _WESetHandleLock((Handle) hWE, saveWELock);
  477.  
  478.     // return result code
  479.     return err;
  480. }
  481.  
  482. pascal OSErr _WESetStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode mode, const WETextStyle *ts, WEHandle hWE)
  483. {
  484.     // alter the style attributes of the specified text range according to ts and mode
  485.  
  486.     WEPtr pWE = *hWE;                    // assume WE record is already locked
  487.     RunArrayHandle hRuns = pWE->hRuns;
  488.     SInt32 offset;
  489.     SInt32 runIndex;
  490.     SInt32 oldStyleIndex, newStyleIndex;
  491.     WERunInfo runInfo;
  492.     Style continuousStyles;
  493.     OSErr err;
  494.  
  495.     WEASSERT(((rangeStart < rangeEnd) || ((rangeStart == 0) && (rangeEnd == 0) && (pWE->textLength == 0))), "¥pBad style range");
  496.  
  497.     // if mode contains weDoToggleFace, we need to determine which Quickdraw styles
  498.     // are continuous over the specified text range: those styles must be turned off
  499.     if (BTST(mode, kModeToggleFace))
  500.     {
  501.         WEStyleMode temp = weDoFace;
  502.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  503.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  504.     }
  505.     else
  506.     {
  507.         continuousStyles = normal;
  508.     }
  509.  
  510.     // find the index to the first style run in the specified range
  511.     offset = rangeStart;
  512.     runIndex = WEOffsetToRun(offset, hWE);
  513.  
  514.     // run thru all the style runs that encompass the selection range
  515.     do
  516.     {
  517.         // find style index for this run and retrieve corresponding style attributes
  518.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  519.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  520.  
  521.         // _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run:
  522.         // correct this anomaly (which is useful for other purposes, anyway)
  523.         if (runInfo.runEnd > pWE->textLength)
  524.         {
  525.             runInfo.runEnd = pWE->textLength;
  526.         }
  527.  
  528.         // apply changes to existing style attributes as requested
  529.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  530.  
  531.         // the high bit of tsFlags must be set if the font
  532.         // belongs to a right-to-left script system
  533.         if (BTST(pWE->flags, weFBidirectional))
  534.         {
  535.             if (GetScriptVariable(FontToScript(runInfo.runAttrs.runStyle.tsFont), smScriptRight) != 0)
  536.             {
  537.                 BSET(runInfo.runAttrs.runStyle.tsFlags, tsRightToLeft);
  538.             }
  539.         }
  540.  
  541.         // get a style index for the new text style
  542.         if ((err = _WENewStyle(&runInfo.runAttrs.runStyle, &newStyleIndex, pWE)) != noErr)
  543.             goto cleanup;
  544.  
  545.         // if offset falls on a style boundary and this style run has become identical
  546.         // to the previous one, merge the two runs together
  547.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  548.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  549.         {
  550.             _WERemoveRun(runIndex, pWE);
  551.             runIndex--;
  552.         }
  553.  
  554.         // style index changed?
  555.         if (oldStyleIndex != newStyleIndex)
  556.         {
  557.             // if offset is in the middle of a style run, insert a new style run in the run array
  558.             if (offset > runInfo.runStart)
  559.             {
  560.                 if ((err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE)) != noErr)
  561.                 {
  562.                     goto cleanup;
  563.                 }
  564.                 runIndex++;
  565.             }
  566.             else
  567.             {
  568.                 // otherwise just change the styleIndex field of the current style run element
  569.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  570.             }
  571.  
  572.             // if specified range ends in the middle of a style run, insert yet another element
  573.             if (rangeEnd < runInfo.runEnd)
  574.             {
  575.                 if ((err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE)) != noErr)
  576.                 {
  577.                     goto cleanup;
  578.                 }
  579.             }
  580.         } // if oldStyle != newStyle
  581.  
  582.         // go to next style run
  583.         runIndex++;
  584.         offset = runInfo.runEnd;
  585.  
  586.     } while (offset < rangeEnd);
  587.  
  588.     // if the last style run ends exactly at the end of the specified range,
  589.     // see if we can merge it with the following style run
  590.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) &&
  591.         ((*hRuns)[runIndex].styleIndex == newStyleIndex))
  592.     {
  593.         _WERemoveRun(runIndex, pWE);
  594.     }
  595.  
  596.     // clear result code
  597.     err = noErr;
  598.  
  599. cleanup:
  600.     // return result code
  601.     return err;
  602. }
  603.  
  604. pascal OSErr _WEApplyStyleScrap(SInt32 rangeStart, SInt32 rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  605. {
  606.     // apply the given style scrap to the specified text range
  607.  
  608.     WEPtr pWE = *hWE;    // assume WE record is already locked
  609.     TEStyleScrapElement *pElement;
  610.     SInt32 runStart, runEnd;
  611.     SInt16 index, lastElement;
  612.     WETextStyle ts;
  613.     OSErr err;
  614.  
  615.     // loop through each element of the style scrap
  616.     lastElement = (*styleScrap)->scrpNStyles - 1;
  617.     for(index = 0; index <= lastElement; index++)
  618.     {
  619.         // get a pointer to the current scrap element
  620.         pElement = (TEStyleScrapElement *) ((*styleScrap)->scrpStyleTab + index);
  621.  
  622.         // calculate text run to which this element is to be applied
  623.         runStart = rangeStart + pElement->scrpStartChar;
  624.         if (index < lastElement)
  625.         {
  626.             runEnd = rangeStart + pElement[1].scrpStartChar;
  627.         }
  628.         else
  629.         {
  630.             runEnd = rangeEnd;
  631.         }
  632.  
  633.         // perform some range checking
  634.         if (runEnd > rangeEnd)
  635.         {
  636.             runEnd = rangeEnd;
  637.         }
  638.         if (runStart >= runEnd)
  639.         {
  640.             continue;
  641.         }
  642.  
  643.         // copy style to a local variable in case memory moves
  644.         * (TextStyle *) &ts = pElement->scrpTEAttrs.runTEStyle;
  645.  
  646.         // apply the specified style to the range
  647.         if ((err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE)) != noErr)
  648.         {
  649.             return err;
  650.         }
  651.     }
  652.     return noErr;
  653. }
  654.  
  655. #if WASTE_OBJECTS
  656.  
  657. pascal OSErr _WEApplySoup(SInt32 offset, Handle hSoup, WEHandle hWE)
  658. {
  659.     WESoup soup;
  660.     Ptr pSoup, pSoupEnd;
  661.     WETextStyle ts;
  662.     Handle hObjectData;
  663.     SInt32 objectOffset;
  664.     Boolean saveWELock;
  665.     OSErr err;
  666.  
  667.     BLOCK_CLR(ts);
  668.     hObjectData = nil;
  669.  
  670.     // lock the WE record
  671.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  672.  
  673.     // lock the soup in high heap
  674.     HLockHi(hSoup);
  675.     pSoup = *hSoup;
  676.     pSoupEnd = pSoup + GetHandleSize(hSoup);
  677.  
  678.     // loop through each object descriptor in the soup
  679.     while (pSoup < pSoupEnd)
  680.     {
  681.         // Object descriptors may be aligned to odd addresses (duh!)
  682.         // this isn't a problem on 68020+ and PPC CPUs, but causes
  683.         // a fatal address error on the 68000.  To avoid this, we
  684.         // copy the descriptor to the stack with BlockMoveData()
  685.         // before trying to access its fields.
  686.         BlockMoveData(pSoup, &soup, sizeof(soup));
  687.  
  688.         // if soupDataSize is negative, this soup is a special type that we won't handle here
  689.         if (soup.soupDataSize < 0)
  690.             continue;
  691.  
  692.         // create a new relocatable block the hold the object data
  693.         if ((err = _WEAllocate(soup.soupDataSize, kAllocTemp, &hObjectData)) != noErr)
  694.             goto cleanup;
  695.  
  696.         // copy the object data to this block
  697.         BlockMoveData(pSoup + sizeof(soup), *hObjectData, soup.soupDataSize);
  698.  
  699.         // create a new object out of the tagged data
  700.         if ((err = _WENewObject(soup.soupType, hObjectData, hWE, &ts.tsObject)) != noErr)
  701.             goto cleanup;
  702.  
  703.         // if there was no new handler for this object, use the object size stored in the soup
  704.         if ((*ts.tsObject)->objectTable == nil)
  705.             (*ts.tsObject)->objectSize = soup.soupSize;
  706.  
  707.         // record a reference to the object descriptor in the style table
  708.         objectOffset = soup.soupOffset + offset;
  709.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  710.         hObjectData = nil;
  711.         ts.tsObject = nil;
  712.         if (err != noErr)
  713.             goto cleanup;
  714.  
  715.         // advance soup pointer
  716.         pSoup += sizeof(soup) + soup.soupDataSize;
  717.  
  718.     } // while
  719.  
  720.     // clear result code
  721.     err = noErr;
  722.  
  723. cleanup:
  724.     // clean up
  725.     HUnlock(hSoup);
  726.     _WEForgetHandle((Handle *) &ts.tsObject);
  727.     _WEForgetHandle(&hObjectData);
  728.  
  729.     // unlock the WE record
  730.     _WESetHandleLock((Handle) hWE, saveWELock);
  731.  
  732.     // return result code
  733.     return err;
  734.  
  735. }
  736.  
  737. #endif    // WASTE_OBJECTS
  738.  
  739. pascal void _WEBumpRunStart(SInt32 runIndex, SInt32 deltaRunStart, WEPtr pWE)
  740. {
  741.     // add deltaLineStart to the lineStart field of all line records
  742.     // starting from lineIndex
  743.  
  744.     RunArrayElementPtr pRun = *pWE->hRuns + runIndex;
  745.     SInt32 nRuns = pWE->nRuns;
  746.  
  747.     // loop through the style run array adjusting the runStart fields
  748.     for ( ; runIndex <= nRuns; runIndex++ )
  749.     {
  750.         pRun->runStart += deltaRunStart;
  751.         pRun++;
  752.     }
  753. }
  754.  
  755. pascal void _WERemoveRunRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  756. {
  757.     // the range of text between rangeStart and rangeEnd is being deleted
  758.     // update the style run array (and the style table) accordingly
  759.  
  760.     WEPtr pWE = *hWE;    // assume WE record is already locked
  761.     RunArrayElementPtr pRun;
  762.     SInt32 startRun, endRun;
  763.  
  764.     // find the index to the first and last style runs in the specified range
  765.     startRun = WEOffsetToRun(rangeStart, hWE);
  766.     endRun = WEOffsetToRun(rangeEnd, hWE) - 1;
  767.  
  768.     // remove all style runs between startRun and endRun
  769.     for ( ; endRun > startRun; endRun-- )
  770.         _WERemoveRun(endRun, pWE);
  771.  
  772.     // move back all subsequent style runs
  773.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  774.  
  775.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  776.     {
  777.         pRun = *pWE->hRuns + endRun;
  778.         pRun[1].runStart = rangeStart;
  779.     }
  780.  
  781.     // remove the first style run if is has become zero length
  782.     pRun = *pWE->hRuns + startRun;
  783.     if (pRun[0].runStart == pRun[1].runStart)
  784.     {
  785.         _WERemoveRun(startRun, pWE);
  786.         startRun--;
  787.     }
  788.  
  789.     // merge the first and last runs if they have the same style index
  790.     if (startRun >= 0)
  791.     {
  792.         pRun = *pWE->hRuns + startRun;
  793.         if (pRun[0].styleIndex == pRun[1].styleIndex)
  794.         {
  795.             _WERemoveRun(startRun + 1, pWE);
  796.         }
  797.     }
  798. }
  799.  
  800. pascal void _WEBumpLineStart(SInt32 lineIndex, SInt32 deltaLineStart, WEPtr pWE)
  801. {
  802.     // add deltaLineStart to the lineStart field of all line records
  803.     // starting from lineIndex
  804.  
  805.     LineRec *pLine = *pWE->hLines + lineIndex;
  806.     SInt32 nLines = pWE->nLines;
  807.  
  808.     // loop through the line array adjusting the lineStart fields
  809.     for ( ; lineIndex <= nLines; lineIndex++ )
  810.     {
  811.         pLine->lineStart += deltaLineStart;
  812.         pLine++;
  813.     }
  814. }
  815.  
  816. pascal void _WERemoveLineRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  817. {
  818.     // the range of text between rangeStart and rangeEnd is being deleted
  819.     // update the line array accordingly
  820.  
  821.     WEPtr pWE = *hWE;    // assume WE record is already locked
  822.     SInt32 startLine, nLines;
  823.  
  824.     // remove all line records between rangeStart and rangeEnd
  825.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  826.     nLines = WEOffsetToLine(rangeEnd, hWE) - startLine + 1;
  827.  
  828.     _WESplice((Handle) pWE->hLines, nil, - (nLines * sizeof(LineRec)), startLine * sizeof(LineRec));
  829.     pWE->nLines -= nLines;
  830.  
  831.     // update the lineStart field of all the line records that follow
  832.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  833. }
  834.  
  835. pascal OSErr _WEDeleteRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  836. {
  837.     // used internally to delete a text range
  838.     WEPtr pWE = *hWE;    // assume WE record is already locked
  839.     WERunInfo runInfo;
  840.     OSErr err = noErr;
  841.  
  842.     if (rangeEnd > pWE->textLength)
  843.         rangeEnd = pWE->textLength;
  844.  
  845.     // do nothing if the specified range is empty
  846.     if (rangeStart == rangeEnd)
  847.         goto cleanup;
  848.  
  849.     // save the first style in the specified range in nullStyle
  850.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  851.     pWE->nullStyle = runInfo.runAttrs;
  852.     BSET(pWE->flags, weFUseNullStyle);
  853.  
  854. #if WASTE_OBJECTS
  855.     // special case: if we're deleting up to the end of the text, see whether
  856.     // there's an embedded object at the very end and remove it
  857.     if (rangeEnd == pWE->textLength)
  858.     {
  859.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  860.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  861.         {
  862.             runInfo.runAttrs.runStyle.tsObject = nil;
  863.             if ((err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE)) != noErr)
  864.                 goto cleanup;
  865.         }
  866.     }
  867. #endif
  868.  
  869.     // remove all line records between rangeStart and rangeEnd
  870.     _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  871.  
  872.     // remove all style runs between rangeStart and rangeEnd
  873.     _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  874.  
  875.     // remove the text
  876.     if ((err = _WESplice(pWE->hText, nil, rangeStart - rangeEnd, rangeStart)) != noErr)
  877.         goto cleanup;
  878.  
  879.     // update textLength field
  880.     pWE->textLength -= (rangeEnd - rangeStart);
  881.  
  882.     // we modified the text, so the anchor range (if any) is no longer valid
  883.     pWE->clickCount = 0;
  884.  
  885. cleanup:
  886.     // return result code
  887.     return err;
  888. }
  889.  
  890. pascal OSErr _WEInsertText(SInt32 offset, Ptr textPtr, SInt32 textLength, WEHandle hWE)
  891. {
  892.     WEPtr pWE = *hWE;    // assume WE record is already locked
  893.     WEStyleMode mode;
  894.     OSErr err = noErr;
  895.  
  896.     // do nothing if textLength is zero or negative
  897.     if (textLength <= 0)
  898.         goto cleanup;
  899.  
  900.     // insert the text
  901.     if ((err = _WESplice(pWE->hText, textPtr, textLength, offset)) != noErr)
  902.         goto cleanup;
  903.  
  904.     // update the lineStart fields of all lines following the insertion point
  905.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  906.  
  907.     // update the runStart fields of all style runs following the insertion point
  908.     _WEBumpRunStart(WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  909.  
  910.     // update the textLength field
  911.     pWE->textLength += textLength;
  912.  
  913.     // we modified the text, so the anchor range (if any) is no longer valid
  914.     pWE->clickCount = 0;
  915.  
  916.     // make sure the newly inserted text doesn't reference any embedded object
  917. #if WASTE_OBJECTS
  918.     pWE->nullStyle.runStyle.tsObject = nil;
  919. #endif
  920.     mode = weDoObject;
  921.  
  922.     // if there is a valid null style, apply it to the newly inserted text
  923.     if (BTST(pWE->flags, weFUseNullStyle))
  924.     {
  925.         mode += (weDoAll + weDoReplaceFace);
  926.     }
  927.  
  928.     if ((err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE)) != noErr)
  929.         goto cleanup;
  930.  
  931. cleanup:
  932.     return err;
  933. }
  934.